package com.sc.nft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.common.util.UuidUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sc.nft.dao.*;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.GasConsumeDTO;
import com.sc.nft.entity.dto.OrderGasShareDTO;
import com.sc.nft.entity.dto.SaasTaskBurialPointDTO;
import com.sc.nft.entity.vo.OrderGasVO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalException;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.MessageQueueHelper;
import com.sc.nft.service.OrderGasService;
import com.sc.nft.sup.Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 刘国航
 * @desc
 * @since 2024/10/21 11:21
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class OrderGasServiceImpl implements OrderGasService {

    private final OrderGasDetailDao orderGasDetailDao;
    private final OrderGasDao orderGasDao;
    private final OrderGasShareConfigDao orderGasShareConfigDao;
    private final UserCollectionDao userCollectionDao;
    private final UserWalletDao userWalletDao;
    private final UserWalletDetailDao userWalletDetailDao;
    private final MessageQueueHelper messageQueueHelper;
    private final Redisson redisson;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void gasConsume(GasConsumeDTO gasConsume) {
        if (gasConsume.getConsumeCost().compareTo(BigDecimal.ZERO)<1){
            return;
        }
        if (gasConsume.getGasConsumeType() == GasConsumeTypeEnum.BALANCE) {
            UserWallet userWallet = userWalletDao.getByUserId(gasConsume.getUserId());
            //扣原石
            BigDecimal subPayBalance = subPayBalance(gasConsume.getUserId(), gasConsume.getWalletDetailType(),userWallet, gasConsume.getConsumeCost());
            //扣余额
            subBalance(gasConsume.getUserId(), gasConsume.getWalletDetailType(), userWallet,subPayBalance);
        }
        
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                OrderGasShareDTO orderGasShareDTO = new OrderGasShareDTO();
                orderGasShareDTO.setCollectionId(gasConsume.getCollectionId());
                orderGasShareDTO.setGas(gasConsume.getConsumeCost());
                orderGasShareDTO.setOrderId(gasConsume.getProductId());
                orderGasShareDTO.setOrderNo(UuidUtils.generateUuid());
                orderGasShareDTO.setOrderType(gasConsume.getOrderGasType());
                orderGasShareDTO.setOrderTime(DateTime.now());
                messageQueueHelper.sendMessage(MessageTopic.ORDER_GAS_SHARE, orderGasShareDTO, MessageTopic.ORDER_GAS_SHARE, orderGasShareDTO.getOrderNo());
            }
        });
      
    }
    
    
    
    //扣减余额
    private void subBalance(Long userId, WalletDetailTypeEnum walletDetailType,UserWallet userWallet,BigDecimal subPayBalance) {
        if (subPayBalance.compareTo(BigDecimal.ZERO) < 1) {//原石已经全都扣了
           return;
        }
        
        if (userWallet.getBalance().compareTo(subPayBalance) <= 0) {
            throw new GlobalRunTimeException("余额不足,上链费扣减失败");
        }
        
        if (!userWalletDao.subBalance(userId, subPayBalance)) {
            throw new GlobalRunTimeException("余额扣减失败!");
        }
        
        UserWalletDetail userWalletDetail = new UserWalletDetail();
        userWalletDetail.setUserId(userWallet.getUserId());
        userWalletDetail.setType(walletDetailType);
        userWalletDetail.setAmount(subPayBalance);
        userWalletDetail.setCharge(BigDecimal.ZERO);
        userWalletDetail.setRealAmount(subPayBalance);
        userWalletDetail.setBeforeBalance(userWallet.getBalance());
        userWalletDetail.setAfterBalance(userWallet.getBalance().subtract(subPayBalance));
        userWalletDetail.setIsAdd(Boolean.FALSE);
        userWalletDetail.setBalanceType(UserBalanceTypeEnum.BALANCE);
        userWalletDetailDao.insert(userWalletDetail);
    }
    
    //扣减原石
    private BigDecimal subPayBalance(Long userId,WalletDetailTypeEnum walletDetailType,UserWallet userWallet, BigDecimal consumeCost) {
        //先看原石是否为0
        BigDecimal currentBalance = userWallet.getPayBalance();
        if (currentBalance.compareTo(BigDecimal.ZERO)<1){
        return consumeCost;
        }
        if (!userWalletDao.subPayBalance(userId,consumeCost)) {
            throw new GlobalRunTimeException("原石扣减失败!");
        }
        UserWalletDetail userWalletDetail = new UserWalletDetail();
        userWalletDetail.setUserId(userWallet.getUserId());
        userWalletDetail.setType(walletDetailType);
        userWalletDetail.setAmount(consumeCost);
        userWalletDetail.setCharge(BigDecimal.ZERO);
        userWalletDetail.setRealAmount(consumeCost);
        userWalletDetail.setBeforeBalance(consumeCost);
        userWalletDetail.setAfterBalance(currentBalance.subtract(consumeCost));
        userWalletDetail.setIsAdd(Boolean.FALSE);
        userWalletDetail.setBalanceType(UserBalanceTypeEnum.PAY_BALANCE);
        userWalletDetailDao.insert(userWalletDetail);
        
        BigDecimal payBalance = userWallet.getPayBalance();
        BigDecimal newPayBalance = payBalance.subtract(consumeCost);
        return newPayBalance.abs();
    }
    
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refreshOrderGasPool() {
        //当前时间
        Date currentDate = new Date();
        //获取未统计上链费
        BigDecimal gas = orderGasDetailDao.getUnCountedBeforeDate(currentDate);
        if (gas.compareTo(BigDecimal.ZERO) > 0) {
            Assert.isTrue(orderGasDao.addGasPool(gas), () -> new GlobalRunTimeException("更新上链费分红池失败"));
            //更新为已统计
            Assert.isTrue(orderGasDetailDao.updateCountedBeforeDate(currentDate), () -> new GlobalRunTimeException("更新上链费分红池失败"));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rewardsOrderGas() {
        //当前时间
        Date currentDate = new Date();
        //本月15号0时0分0秒
        Date fifteenthDate = fifteenthDate();
        if (currentDate.compareTo(fifteenthDate) < 0) {
            return;
        }
        //获取未结算上链费
        BigDecimal unSettledBeforeDate = orderGasDetailDao.getUnSettledBeforeDate(fifteenthDate);
        if (unSettledBeforeDate.compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        BigDecimal rewardGas = rewardGas(unSettledBeforeDate, currentDate);
        //发放上链费分红
        Assert.isTrue(orderGasDao.addReceiveRewards(unSettledBeforeDate, rewardGas), "发放上链费分红失败");
        //更新未已结算
        Assert.isTrue(orderGasDetailDao.updateSettledBeforeDate(fifteenthDate), () -> new GlobalRunTimeException("发放上链费分红失败"));
    }

    @Override
    public OrderGasVO getGas() {
        OrderGasVO orderGasVO = new OrderGasVO();
        orderGasVO.setGasPool(BigDecimal.ZERO);
        orderGasVO.setGasReceiveTotal(BigDecimal.ZERO);
        orderGasVO.setGasRewardsTotal(BigDecimal.ZERO);
        OrderGas orderGas = orderGasDao.getOne(new LambdaQueryWrapper<>());
        if (Objects.nonNull(orderGas)) {
            orderGasVO.setGasPool(orderGas.getGasPool());
            orderGasVO.setGasReceiveTotal(orderGas.getGasReceiveTotal());
            orderGasVO.setGasRewardsTotal(orderGas.getGasRewardsTotal());
        }
        return orderGasVO;
    }


    /**
     * 本月15号0时0分0秒
     *
     * @return
     */
    private Date fifteenthDate() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, 15);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 发放上链费分红
     *
     * @return
     */
    private BigDecimal rewardGas(BigDecimal sumConsumeGas, Date currentDate) {
        BigDecimal rewardGas = BigDecimal.ZERO;
        List<OrderGasShareConfig> orderGasShareConfigList = orderGasShareConfigDao.listByStatus(Boolean.TRUE);
        if (CollectionUtil.isEmpty(orderGasShareConfigList)) {
            return rewardGas;
        }
        Map<Long, OrderGasShareConfig> orderGasShareConfigMap = orderGasShareConfigList.stream().collect(Collectors.toMap(OrderGasShareConfig::getCollectionId, Function.identity()));
        List<UserCollection> userCollectionList = userCollectionDao.selectByCollectionIdsIsHold(new ArrayList<>(orderGasShareConfigMap.keySet()));
        Map<Long, List<UserCollection>> userCollectionMap = CollectionUtil.isNotEmpty(userCollectionList) ? userCollectionList.stream().collect(Collectors.groupingBy(UserCollection::getCollectionId)) : new HashMap();
        for (Map.Entry<Long, List<UserCollection>> entry : userCollectionMap.entrySet()) {
            OrderGasShareConfig orderGasShareConfig = orderGasShareConfigMap.get(entry.getKey());
            BigDecimal totalGas = sumConsumeGas.multiply(orderGasShareConfig.getShareRatio().divide(new BigDecimal(100)));
            int holdCount = entry.getValue().size();
            BigDecimal singleGas = totalGas.divide(new BigDecimal(holdCount), 2, RoundingMode.FLOOR);
            OrderGasRewardsTotal orderGasRewardsTotal = new OrderGasRewardsTotal();
            orderGasRewardsTotal.setCollectionId(entry.getKey());
            orderGasRewardsTotal.setShareRatio(orderGasShareConfig.getShareRatio());
            orderGasRewardsTotal.setTotalGas(totalGas.setScale(2, RoundingMode.FLOOR));
            orderGasRewardsTotal.setHoldCount(holdCount);
            orderGasRewardsTotal.setSingleGas(singleGas);
            orderGasRewardsTotal.setRewardsTime(currentDate);
            orderGasRewardsTotal.insert();

            Map<Long, List<UserCollection>> collectMap = entry.getValue().stream().collect(Collectors.groupingBy(UserCollection::getUserId));
            for (Map.Entry<Long, List<UserCollection>> userEntry : collectMap.entrySet()) {
                BigDecimal gas = singleGas.multiply(new BigDecimal(userEntry.getValue().size()));
                rewardGas = rewardGas.add(gas);
                orderGasShareProfit(userEntry.getKey(), gas, orderGasShareConfig.getShareName());
                OrderGasRewardsRecord orderGasRewardsRecord = new OrderGasRewardsRecord();
                orderGasRewardsRecord.setUserId(userEntry.getKey());
                orderGasRewardsRecord.setCollectionId(entry.getKey());
                orderGasRewardsRecord.setHoldCount(userEntry.getValue().size());
                orderGasRewardsRecord.setGas(gas);
                orderGasRewardsRecord.setRewardsTime(currentDate);
                orderGasRewardsRecord.insert();
            }
        }

        OrderGasRecord orderGasRecord = new OrderGasRecord();
        orderGasRecord.setGasReceiveTotal(sumConsumeGas);
        orderGasRecord.setGasRewardsTotal(rewardGas);
        orderGasRecord.setRewardsTime(currentDate);
        orderGasRecord.insert();

        return rewardGas;
    }

    public void orderGasShareProfit(Long userId, BigDecimal gas, String shareName) {
        UserWallet upUserWallet = userWalletDao.getByUserId(userId);

        // 把返现金额给用户
        userWalletDao.addBalance(userId, gas);

        UserWalletDetail userWalletDetail = new UserWalletDetail();
        userWalletDetail.setUserId(userId);
        userWalletDetail.setType(WalletDetailTypeEnum.ORDER_GAS_SHARE);
        userWalletDetail.setBalanceType(UserBalanceTypeEnum.BALANCE);
        userWalletDetail.setAmount(gas);
        userWalletDetail.setCharge(BigDecimal.ZERO);
        userWalletDetail.setRealAmount(gas);
        userWalletDetail.setAfterBalance(upUserWallet.getPayBalance().add(gas));
        userWalletDetail.setBeforeBalance(upUserWallet.getPayBalance());
        userWalletDetail.setIsAdd(Boolean.TRUE);
        userWalletDetail.setRemark(WalletDetailTypeEnum.ORDER_GAS_SHARE.getDescription());
        userWalletDetailDao.insert(userWalletDetail);

        UserIncomeRecord userIncomeRecord = new UserIncomeRecord();
        userIncomeRecord.setAmount(gas);
        userIncomeRecord.setUserId(userId);
        userIncomeRecord.setIncomeType(IncomeTypeEnum.ORDER_GAS_SHARE);
        userIncomeRecord.setIncomeTypeName(shareName);
        userIncomeRecord.setIncomeHierarchy(IncomeHierarchyEnum.SPECIAL);
        userIncomeRecord.setIncomeHierarchyName(IncomeHierarchyEnum.SPECIAL.getDescription());
        userIncomeRecord.setOrderNo("");
        userIncomeRecord.setSourceUserId(0L);
        userIncomeRecord.setIncomeUserNum("1");
        userIncomeRecord.setDescribe(IncomeTypeEnum.ORDER_GAS_SHARE.getDescription());
        userIncomeRecord.insert();

    }

}
